<?php
namespace Common;

class Validator {

	const WRONG_LENGTH = 0;
	const WRONG_CONTROL_NUMBER = 1;
	const WRONG_FORMAT = 2;
	const FUTURE_MAN = 3;

	
	

	/**
	 * Validerar email
	 *
	 * @param string $email
	 * @return boolean
	 */
	public static function validateEmail($email) {
		$regex = '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/';

		// Ersätter '[a]' med '@' och kollar mot det reguljära uttrycket
		return preg_match($regex, str_replace("[a]", "@", $email)) == 0 ? false : true;
	}


	/**
	 * Validerar personnummer enligt luhn
	 *
	 * @param string $ssnr
	 * @return int | string
	 */
	public static function validateSocialSecNr($ssnr) {
		$error = null;
		$trimPattern = '/[^0-9]/';
		$formatPattern = '/^((19|20){0,1})([0-9]{2})((0[1-9]|1[012]){1})((0[1-9]|[12][0-9]|3[01]){1})(-{0,1})([0-9]{4})/';
		$replacement = '';

		// Är personnummret i rätt format?
		if (preg_match($formatPattern, $ssnr) == 0) {
			$error = self::WRONG_FORMAT;
		} else {
			// Ersätt tecken(-) med '' (inget)
			$ssnr = preg_replace($trimPattern, $replacement, $ssnr);

			// Rätt längd på personnummer?
			if (strlen($ssnr) != 10 && strlen($ssnr) != 12) {
				$error = self::WRONG_LENGTH;
			} else {

				// Är århundradet med i personnummret?
				if (strlen($ssnr) == 12) {

					//Kollar vi så personen inte fyllt i ett datum i framtiden
					if (self::checkPresentDate(substr($ssnr, 6)) === false) {
						$error = self::FUTURE_MAN;
					} else {

						//Ta bort århundrandet
						$ssnr = substr($ssnr, 2);

						// Fungerar Luhn-algoritmen
						if (!(self::luhn($ssnr))) {
							$error = self::WRONG_CONTROL_NUMBER;
						}
					}
				}
			}
		}

		// Retunerar ett korrekt personnummer eller felkod
		return $error !== null ? $error : $ssnr;
	}

	/**
	 * Testar Luhn-algoritmen
	 *
	 * @param string $inp
	 * @return boolean
	 * @link http://sv.wikipedia.org/wiki/Luhn-algoritmen
	 */
	private static function luhn($inp) {

		$sum = 0;
		// Konverterar till array
		$input = str_split($inp);

		// Loopar igenom 'strängen' och multiplicera med 1 varannan gång
		// och 2 varannan gång.
		for ($i = 0; $i < count($input); $i++) {
			if ($i % 2 == 0) {
				$x = $input[$i] * 2;
				$x > 9 ? $sum += $x - 9 : $sum += $x;
			} else {
				$sum += $input[$i];
			}
		}

		// Jämnt delbart med 10?
		return ($sum % 10 == 0) ? true : false;
	}

	/**
	 * Kollar om medskickat datum ligger i framtiden
	 *
	 * @param string $date
	 * @return boolean
	 */
	private static function checkPresentDate($date) {
		return strtotime(date("Ymd")) > strtotime($date) ? true : false;
	}

	/**
	 * Validerar datum
	 *
	 * @param string $date
	 * @return String | Integer
	 */
	public static function validateDate($date) {
		$formatHyphenPattern = '/^((19|20){0,1})([0-9]{2})(-{1})((0[1-9]|1[012]){1})(-{1})((0[1-9]|[12][0-9]|3[01]){1})/';
		$formatPattern = '/^((19|20){0,1})([0-9]{2})((0[1-9]|1[012]){1})((0[1-9]|[12][0-9]|3[01]){1})/';
		$error = null;

		// Kollar så datum följer visst format (regexp)
		if (preg_match($formatPattern, $date) == 0 && preg_match($formatHyphenPattern, $date) == 0) {
			$error = self::WRONG_FORMAT;
		}

		// Tar bort bindestreck
		$date = str_replace("-", "", $date);

		// Hämtar ut längden och kollar så den är rätt
		$length = strlen($date);
		if ($length !== 6 && $length !== 8) {
			$error = self::WRONG_LENGTH;
		}

		// Kollar om århundradet finns med...
		if ($length == 8) {
			// ... och tar bort det
			$date = substr($date, 2);
		}

		// Lägger till bindestreck
		$returnDate = "";
		foreach (str_split($date) as $pos => $d) {
			if ($pos % 2 == 0 && $pos !== 0) {
				$returnDate .= "-$d";
			} else {
				$returnDate .= $d;
			}
		}

		// Retunerar ett korrekt datum eller felkod
		return $error !== null ? $error : $returnDate;
	}

	/**
	 * Filtrerar bort javascript-taggar och tillåter viss HTML
	 * 
	 * @param string $html
	 * @return string
	 */
	public static function filterJavascript($html) {
		$allowed_tags = "<div><tt><span><a><p><h1><h2><h3><ul><li><ol>";
		return strip_tags($html, $allowed_tags);
	}

	/**
	 * Filtrerar bort alla taggar ifrån en sträng
	 * 
	 * @param string $html
	 * @return string
	 */
	public static function filterHTML($html) {
		return strip_tags($html);
	}

	/**
	 * Kontrollerar ett lösenord uppfyller våra domänregler
	 * 
	 * @param string $pw
	 * @return boolean
	 */
	public static function validatePassword($pw) {
		$pattern = '/^.*(?=.{6,12})(?=.*\d)(?=.*[a-z]).*$/';
		return preg_match($pattern, $pw) == 0 ? false : true;
	}
	
	/**
	 * Kontrollerar ett användarnamn uppfyller våra domänregler
	 * 
	 * @param string $username
	 * @return boolean
	 */
	public static function validateUsername($username) {
		$regex = '/^[A-Za-z0-9_-]{3,15}$/';
		return preg_match($regex, $username) == 0 ? false : true;
	}
	
	/**
	 * Kontrollerar att ett nummer kan tolkas som ett nummer
	 * 
	 * @param string | i0nteger $nr
	 * @return boolean
	 */
	public static function validateNumber($nr) {
		return is_numeric($nr);
	}


	/**
	 * Funktion som testar alla våra funktioner
	 * 
	 * @return boolean
	 */
	public function test() {

		/**
		 * Test av email
		 */
		$correctEmail = "emillindau@gmail.com";
		$nonCorrectEmails = array("a", "a.a", "a..a@gmail.com", "@gmail.com", "@", "test@co.uk.");

		if (!(self::validateEmail($correctEmail))) {
			return false;
		}

		foreach ($nonCorrectEmails as $wrongMail) {
			if (self::validateEmail($wrongMail)) {
				return false;
			}
		}

		/**
		 * Test av personnummer
		 */

		// Testar korrekta personnummer
		$correctSsnrs = array("199002034396", "19900203-4396", "9002034396", "900203-4396");
		foreach ($correctSsnrs as $ssnr) {
			if (self::validateSocialSecNr($ssnr) != "9002034396") {
				return false;
			}
		}

		// Futureman-test
		$wrongSsnrs = array("20121224-3434", "205665452323");
		foreach ($wrongSsnrs as $ssnr) {
			if (strlen(self::validateSocialSecNr($ssnr)) == self::FUTURE_MAN) {
				return false;
			}
		}

		/**
		 * Test av datum
		 */

		// Testar korrekta datum
		$correctDates = array("1990-02-03", "900203", "90-02-03");
		foreach ($correctDates as $date) {
			if (self::validateDate($date) != "90-02-03") {
				return false;
			}
		}

		// Testar felaktiga datum
		$wrongDates = array("2012-12-32", "99231243", "00-00-00", "1765-99-23");
		foreach ($wrongDates as $date) {
			if (strlen(self::validateDate($date)) !== 1) {
				return false;
			}
		}

		/**
		 * Test av javascriptfiltrering
		 */

		// Testar att ta bort javascript-tags
		$allowed_html = "<h2>javascript</h2>";
		$html = "<script type='text/javascript'>$allowed_html</script>";
		if (self::filterJavascript($html) !== $allowed_html) {
			return false;
		}

		/**
		 * Test av HTML
		 */

		// Testar att ta bort alla taggar
		$testArray = array("lol" => "nisse<h2></h2>");
		$html = "<div><h2>Testar HTML</h2></div>";
		if (self::filterHTML($html) === $html) {
			return false;
		}

		/**
		 * Test av lösenord
		 */

		//Testa felaktiga lösenord
		$wrongPasswords = array("Aa3", "magsvett", "m4gsv3tt", "111111111111111aaaaaaaaaaaaaagggggggg");
		foreach ($wrongPasswords as $password) {
			if (self::validatePassword($password) == true) {
				return false;
			}
		}

		//Testar korrekt lösenord
		if (self::validatePassword("k0rrektP4ss") == false) {
			return false;
		}

		/**
		 * Test av nummer
		 */

		//Testar array av giltiga tal
		$correctNumbers = array("9", "9.0", 9, -9.0, 1e0);

		foreach ($correctNumbers as $number) {
			if (self::validateNumber($number) !== true) {
				return false;
			}
		}

		//Testar array av ogiltiga tal
		$wrongNumbers = array("e9", "nio", "over9000", "9..0");

		foreach ($wrongNumbers as $number) {
			if (self::validateNumber($number) === true) {
				return false;
			}
		}

		return true;
	}

}
